package com.toubutu.audio.auth.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.toubutu.audio.admin.dto.LoginDto;
import com.toubutu.audio.admin.dto.RegisterDto;
import com.toubutu.audio.admin.dto.UserRoleBindDto;
import com.toubutu.audio.admin.entity.User;
import com.toubutu.audio.admin.mapper.UserMapper;
import com.toubutu.audio.admin.service.RoleService;
import com.toubutu.audio.admin.vo.LoginUserInfo;
import com.toubutu.audio.admin.vo.RoleVo;
import com.toubutu.audio.admin.vo.UserAuthVo;
import com.toubutu.audio.auth.service.UserAuthService;
import com.toubutu.audio.common.BusinessConstants;
import com.toubutu.audio.context.HttpServletRequestContext;
import com.toubutu.audio.dto.ChangeBindEmailDto;
import com.toubutu.audio.dto.UpdatePasswordDto;
import com.toubutu.audio.dto.UserProfileDto;
import com.toubutu.common.BusinessException;
import com.toubutu.common.CurrentUserContext;
import com.toubutu.common.StatusCodeEnum;
import com.toubutu.constants.RedisKey;
import com.toubutu.entity.TokenEntity;
import com.toubutu.jwt.JwtHandler;
import com.toubutu.properties.JwtProperties;
import com.toubutu.util.RedisService;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 戒躁
 * @version 1.0
 * @date 2023年10月30日 15:54
 */
@Service
public class UserAuthServiceImpl extends ServiceImpl<UserMapper, User> implements UserAuthService {
    @Resource
    private JwtHandler jwtHandler;
    @Resource
    private RedisService redisService;
    @Resource
    private JwtProperties jwtProperties;
    @Resource
    private RoleService roleService;

    @Value("${avatar}")
    private String defaultAvatar;
    @Override
    public TokenEntity passwordLogin(LoginDto loginDto) {
        //1.查询是否存在账号
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getEmail,loginDto.getAccount())
                .eq(User::getSource,HttpServletRequestContext.isFromAdmin()?BusinessConstants.USER_SOURCE_ADMIN:BusinessConstants.USER_SOURCE_FRONT);
        User user = getOne(wrapper);
        if (user==null){
            throw new BusinessException(StatusCodeEnum.AUTHENTICATION_FAILURE);
        }
        //2.校验密码
        String encodePwd = encode(loginDto.getPassword());
        if (!encodePwd.equals(user.getPassword())){
            throw new BusinessException(StatusCodeEnum.AUTHENTICATION_FAILURE);
        }
        return jwtHandler.createTokenEntity(user.getUserId());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void userRegister(RegisterDto registerDto) {
        String redisCode = redisService.get(RedisKey.EMAIL_CODE + registerDto.getAccount()).toString();
        if (!registerDto.getCode().equals(redisCode)){
            throw new BusinessException(StatusCodeEnum.CODE_ERROR);
        }
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getEmail,registerDto.getAccount());
        User user = getOne(wrapper);
        if (user!=null){
            throw new BusinessException(StatusCodeEnum.MAIL_REGISTERED);
        }

        String pwd = encode(registerDto.getPassword());
        User register = new User();
        long userId = RandomUtil.randomLong(100000, 999999);
        register.setUserId(userId);
        register.setAvatar(defaultAvatar);
        register.setNickName("用户"+ userId);
        register.setEmail(registerDto.getAccount());
        register.setPassword(pwd);
        register.setSource(HttpServletRequestContext.isFromAdmin()?BusinessConstants.USER_SOURCE_ADMIN:BusinessConstants.USER_SOURCE_FRONT);
        saveOrUpdate(register);
        UserRoleBindDto userRoleBindDto = new UserRoleBindDto();
        userRoleBindDto.setUserId(userId);
        if (HttpServletRequestContext.isFromAdmin()){
            //分配权限,后台注册默认为管理员
            register.setNickName("管理员"+ userId);
            userRoleBindDto.setRoleIds(Collections.singletonList(BusinessConstants.ADMIN_ROLE));
        }else {
            //前台注册默认为顾客
            register.setNickName("用户"+ userId);
            userRoleBindDto.setRoleIds(Collections.singletonList(BusinessConstants.MEMBER_ROLE));
        }

    }

    @Override
    public UserAuthVo getLoginUserInfo(String accessToken,String from) {
        LoginUserInfo loginUserInfo  = (LoginUserInfo) CurrentUserContext.getCurrentUser();
        UserAuthVo userAuthVo = new UserAuthVo();
        if (loginUserInfo!=null){
            List<RoleVo> roleList = loginUserInfo.getRoleList();
            BeanUtils.copyProperties(loginUserInfo,userAuthVo);
            if (!CollectionUtils.isEmpty(roleList)){
               userAuthVo.setRoles( roleList.stream().map(RoleVo::getRoleTag).collect(Collectors.toList()));
            }

            return userAuthVo;
        }
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        Object userId = jwtHandler.parseToken(accessToken);
        wrapper.eq(User::getUserId,userId);
        User user = getOne(wrapper);
        if (user==null){
            throw new BusinessException(StatusCodeEnum.USERNAME_NOT_EXIST);
        }
        userAuthVo.setUserId(user.getUserId());
        doAuthFrom(userAuthVo);
        LoginUserInfo authUserInfo = new LoginUserInfo();
        authUserInfo.setPermissionList(userAuthVo.getPermissionList());
        authUserInfo.setRoleList(userAuthVo.getRoleList());
        BeanUtils.copyProperties(user,authUserInfo);
        authUserInfo.setUserId(user.getUserId());
        BeanUtils.copyProperties(user,userAuthVo);
        userAuthVo.setUserId(user.getUserId());
        redisService.set(jwtProperties.getAuthLoginUserInfoKey()+userId,authUserInfo);
        return userAuthVo;
    }



    @Override
    public void updateUserInfo(UserProfileDto userProfileDto) {
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getUserId,userProfileDto.getUserId());
        if (StringUtils.isNotBlank(userProfileDto.getNickName())){
            wrapper.set(User::getNickName,userProfileDto.getNickName());
        }
        if (StringUtils.isNotBlank(userProfileDto.getAvatar())){
            wrapper.set(User::getAvatar,userProfileDto.getAvatar());
        }
        if (userProfileDto.getBirthday()!=null){
            wrapper.set(User::getBirthday,userProfileDto.getBirthday());
        }
        if (StringUtils.isNotBlank(userProfileDto.getAvatar())){
            wrapper.set(User::getAvatar,userProfileDto.getAvatar());
        }
        if (userProfileDto.getGender()!=null){
            wrapper.set(User::getGender,userProfileDto.getGender());
        }
        update(wrapper);
        //删除缓存
        String userInfoKey = jwtProperties.getAuthLoginUserInfoKey() + userProfileDto.getUserId();
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserId,userProfileDto.getUserId());
        User user = getOne(queryWrapper);
        LoginUserInfo authUserInfo = new LoginUserInfo();
        BeanUtils.copyProperties(user,authUserInfo);
        redisService.set(userInfoKey,authUserInfo);
    }

   private void doAuthFrom(UserAuthVo userAuthVo){
       //获取角色信息
       List<RoleVo> userRoles = roleService.getUserRoles(userAuthVo.getUserId());
       userAuthVo.setRoleList(userRoles);
       userAuthVo.setRoles(userRoles.stream().map(RoleVo::getRoleTag).collect(Collectors.toList()));
       //获取拥有的权限
       userAuthVo.setPermissionList(userRoles.isEmpty()? Collections.emptyList()
             :roleService.getPermissionCodeByRoleIds(userRoles.stream().map(RoleVo::getRoleId).collect(Collectors.toList())));
   }

    @Override
    public void logout(String accessToken) {
        LoginUserInfo loginUserInfo  = (LoginUserInfo) CurrentUserContext.getCurrentUser();
        if (loginUserInfo!=null){
            List<String> keys = new ArrayList<>(3);
            keys.add(jwtProperties.getAuthLoginUserInfoKey()+loginUserInfo.getUserId());
            keys.add(jwtProperties.getAccessTokenKey()+loginUserInfo.getUserId());
            keys.add(jwtProperties.getRefreshTokenKey()+loginUserInfo.getUserId());
            redisService.del(keys);
        }
    }

    @Override
    public void updateBindEmail(ChangeBindEmailDto bindEmailDto) {
        //校验新邮箱验证码
        Object code = redisService.get(RedisKey.EMAIL_CODE + bindEmailDto.getNewEmail());
        if (code==null || !code.toString().equals(bindEmailDto.getCode())){
            throw new BusinessException(StatusCodeEnum.CODE_ERROR);
        }
        //校验邮箱是否已存在
        LambdaUpdateWrapper<User> queryWrapper = new LambdaUpdateWrapper<>();
        queryWrapper.eq(User::getEmail,bindEmailDto.getNewEmail());
        User one = getOne(queryWrapper);
        if (one!=null){
            throw new BusinessException(StatusCodeEnum.USER_HANDLE.getCode(),"邮箱已存在!");
        }
        String userId = CurrentUserContext.getUserId();
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(User::getUserId,userId)
                .set(User::getEmail,bindEmailDto.getNewEmail());
        update(updateWrapper);
    }

    @Override
    public void updatePassword(UpdatePasswordDto updatePasswordDto) {
        String userId = CurrentUserContext.getUserId();
        //校验旧密码
        LambdaUpdateWrapper<User> queryWrapper = new LambdaUpdateWrapper<>();
        queryWrapper.eq(User::getUserId,userId);
        User user = getOne(queryWrapper);
        if (!encode(updatePasswordDto.getOldPassword()).equals(user.getPassword())){
            throw new BusinessException(StatusCodeEnum.USER_HANDLE.getCode(),"旧密码错误!");
        }
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(User::getUserId,userId)
                .set(User::getPassword,encode(updatePasswordDto.getNewPassword()));
        update(updateWrapper);
    }

    private  String encode(String str){
        try {
            MessageDigest instance = MessageDigest.getInstance("SHA-1");
            instance.update(str.getBytes());
            byte[] digest = instance.digest();
            return String.valueOf(Hex.encodeHex(digest));
        }catch (Exception e){
            e.printStackTrace();
            return "";
        }
    }


}
